package org.jackysoft.service.impl;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Property;
import org.jackysoft.data.SortInfo;
import org.jackysoft.entity.FileItem;
import org.jackysoft.entity.Folder;
import org.jackysoft.entity.HistoryFileItem;
import org.jackysoft.entity.ReceivedFileItem;
import org.jackysoft.entity.UploadedFileItem;
import org.jackysoft.entity.User;
import org.jackysoft.file.CleanFileWork;
import org.jackysoft.file.DetachedCriteriaRoom;
import org.jackysoft.file.FileType;
import org.jackysoft.query.Query;
import org.jackysoft.query.QueryField;
import org.jackysoft.service.AbstractStringIdServiceImpl;
import org.jackysoft.service.FileDataService;
import org.jackysoft.util.CollectionUtils;
import org.jackysoft.util.IOUtils;
import org.jackysoft.util.Page;
import org.jackysoft.util.SpringSecurityUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

@Service
@Transactional
public class FileDataServiceImpl extends AbstractStringIdServiceImpl<FileItem>
		implements FileDataService {

	/**
	 * 
	 */
	private static final long serialVersionUID = 7769094211016454111L;
	protected static final Log logger = LogFactory
			.getLog(FileDataServiceImpl.class);

	@Override
	public void remove(Set<FileItem> files, FileType ft)
			throws DataAccessException {		
		switch (ft) {
		case UPLOADED:
			removeUploadFile(files);
			break;
		case RECEIVED:
			removeReceivedFile(files);
			break;
		default:
			;
		}

	}

	void removeReceivedFile(Set<FileItem> files) throws DataAccessException {
		Session sess = getSession();
		Set<String> ids = Sets.newHashSet();
		for (FileItem f : files)
			ids.add(f.getId());
		String hql = "delete from ReceivedFileItem where id in (:id)";
		sess.createQuery(hql).setParameterList("ids", ids).executeUpdate();

	}

	void removeUploadFile(Set<FileItem> files) throws DataAccessException {
		Session sess = getSession();
		Set<String> ids = Sets.newHashSet();
		for (FileItem f : files)
			ids.add(f.getId());
		String hql = "delete from UploadedFileItem where id in (:id)";
		sess.createQuery(hql).setParameterList("ids", ids).executeUpdate();

	}

	@Override
	public void share(Set<FileItem> files, FileType ft)
			throws DataAccessException {
		switch (ft) {
		case UPLOADED:
			shareUploadFiles(files);
			break;
		case RECEIVED:
			shareReceivedFiles(files);
			break;
		default:
			;
		}

	}

	void shareReceivedFiles(Set<FileItem> files) throws DataAccessException {
		Session sess = getSession();
		Set<String> ids = Sets.newHashSet();
		for (FileItem f : files)
			ids.add(f.getId());
		String hql = "update ReceivedFileItem u set u.shared=true where u.id in (:ids)";
		sess.createQuery(hql).setParameterList("ids", ids).executeUpdate();

	}

	void shareUploadFiles(Set<FileItem> files) throws DataAccessException {
		Set<String> ids = Sets.newHashSet();
		for (FileItem f : files)
			ids.add(f.getId());
		Session sess = getSession();
		String hql = "update UploadedFileItem u set u.shared=true where u.id in (:ids)";
		sess.createQuery(hql).setParameterList("ids", ids).executeUpdate();

	}

	@Override
	public void clear(int year, int month) throws DataAccessException {
		Session sess = getSession();
		CleanFileWork cwork = new CleanFileWork();
		cwork.year = year;
		cwork.month = month;
		log.info("准备清理 " + year + "年 " + month + "月 之前的文件..");
		sess.doWork(cwork);
		Runtime runtime = Runtime.getRuntime();
		for (String p : cwork.paths) {
			String cmd = "cmd.exe /c del /f /q " + p;
			log.info("执行系统命令: " + cmd);
			try {
				Process pcs = runtime.exec(cmd);

				StringWriter sw = new StringWriter();
				IOUtils.copy(pcs.getInputStream(), sw, "GBK");

				log.info(sw.toString());
			} catch (IOException e) {
				log.info("删除文件出现错误: " + e.getMessage());
			} finally {

			}
		}
		sess.clear();
		log.info("物理删除文件完毕...");

	}

	@Override
	public void removeByIdz(Collection<String> fileIds)
			throws DataAccessException {
		String hql = "delete from UploadedFileItem where id in (:ids)";
		String hql2 = "delete from ReceivedFileItem where id in (:ids)";
		CollectionUtils.notNullable(fileIds);
		getSession().createQuery(hql).setParameterList("ids", fileIds)
				.executeUpdate();
		getSession().createQuery(hql2).setParameterList("ids", fileIds)
				.executeUpdate();

	}

	@Override
	@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
	public void remove(User owner, FileType ft) throws DataAccessException {
		switch (ft) {
		case UPLOADED:
			deleteUploadFiles(owner);
			break;
		case RECEIVED:
			deleteReceivedFiles(owner);
			break;
		default:
			;
		}

	}

	void deleteUploadFiles(User owner) throws DataAccessException {
		log.info("清除用户上传文件....");
		String hql = "delete from UploadedFileItem where owner=:owner";
		getSession().createQuery(hql).setParameter("owner", owner)
				.executeUpdate();
		log.info("清除用户上传文件....[OK]");
	}

	void deleteReceivedFiles(User owner) throws DataAccessException {
		log.info("清除用户已传文件....");
		String hql = "delete from ReceivedFileItem where owner=:owner";
		getSession().createQuery(hql).setParameter("owner", owner)
				.executeUpdate();
		hql = "update ReceivedFileItem set fromer=null where fromer=:fromer";
		getSession().createQuery(hql).setParameter("fromer", owner)
				.executeUpdate();
		log.info("清除用户已传文件....[OK]");
	}

	@Override
	protected int listDataSize(SortInfo sortInfo, Map<String, Object> smap) {
		Session session = getSession();
		Folder folder = (Folder) smap.get(Folder.class.getName());
		User owner = SpringSecurityUtils.getCurrentUser();
		DetachedCriteria criteria = DetachedCriteriaRoom.getInstance()
				.getCountDetachedCriteria(folder.getType(),
						owner.getUsername(), folder.getId(), "id");
		criteria.setProjection(Property.forName("id").count());
		Criteria crt = criteria.getExecutableCriteria(session);
		Object o = crt.uniqueResult();
		return o == null ? 0 : Integer.decode(o.toString());
	}

	@Override
	protected List<FileItem> listData(int start, int size, boolean listAll,
			SortInfo sortInfo, Map<String, Object> smap) {
		Session session = getSession();
		Folder folder = (Folder) smap.get(Folder.class.getName());
		User owner = SpringSecurityUtils.getCurrentUser();
		DetachedCriteria criteria = DetachedCriteriaRoom.getInstance()
				.createDetachedCriteria(folder.getType(), owner.getUsername(),
						folder.getId());

		Criteria crt = criteria.getExecutableCriteria(session);
		if (!listAll) {
			crt.setFirstResult(start);
			crt.setMaxResults(size);

		}
		List list = crt.list();
		return crt.list();
	}
	
	Map<FileType, String> fileTypeMaps = new HashMap<FileType,String>(){
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		{
		     put(FileType.HISTORY,table(HistoryFileItem.class));	
		     put(FileType.RECEIVED,table(ReceivedFileItem.class));	
		     put(FileType.SENT,table(ReceivedFileItem.class));	
		     put(FileType.UPLOADED,table(UploadedFileItem.class));	
		}
	};
	

	@Override
	public FileItem[] getsAllByID(Collection<String> ids,String fileType)
			throws DataAccessException {
		if (ids == null || ids.isEmpty())
			return new FileItem[0];
		FileType ftype = FileType.getFileType(fileType);
		String table = fileTypeMaps.get(ftype);
		
		List<FileItem> files = Lists.newArrayList();

		String hql = String
				.format("select new org.jackysoft.entity.FileItem(r.id,r.title,r.fileType) from %s r where r.id in (:id)"
						,table
						);
		List list = getSession().createQuery(hql).setParameterList("id", ids)
				.list();
		files.addAll(list);		
		FileItem[] fs = new FileItem[files.size()];
		files.toArray(fs);
		return fs;
	}

	@Override
	public FileItem getForEdit(String id) throws DataAccessException {
		String hql = String
				.format("select new org.jackysoft.entity.FileItem(r.id,r.title,r.fileType) from ReceivedFileItem  r where r.id='%s'",
						id);
		Iterator itr = query(hql).iterate();
		if (itr.hasNext())
			return (FileItem) itr.next();
		hql = String
				.format("select new org.jackysoft.entity.FileItem(r.id,r.title,r.fileType) from UploadedFileItem  r where r.id='%s'",
						id);
		itr = query(hql).iterate();
		return (FileItem) itr.next();

	}

	@Override
	@SuppressWarnings("rawtypes")
	public Page query(Query qc, Integer pindex, Integer rows)
			throws DataAccessException {
		if (qc == null)
			throw new IllegalArgumentException(
					"argument query must not be null");
		QueryField qf = qc.getFields().get(0);
		String type = qf.getValue();
		FileType ft = FileType.valueOf(type);
		User owner = SpringSecurityUtils.getCurrentUser();
		int start = pindex * rows;
		start = start < 0 ? 0 : start;
		Page page = new Page();
		page.setOffset(rows);
		page.setPage(pindex);

		if (FileType.RECEIVED == ft) {
			String hql = "from ReceivedFileItem f where f.owner=:owner order by f.createTime desc";
			List list = query(hql).setParameter("owner", owner)
					.setFirstResult(start).setMaxResults(rows).list();
			page.setRows(list);
			hql = "select count(*) from ReceivedFileItem f where f.owner=:owner";
			Object lo = query(hql).setParameter("owner", owner).uniqueResult();
			page.setRecords(Integer.parseInt(lo.toString()));

		} else if (FileType.SENT == ft) {
			String hql = "from ReceivedFileItem f where f.fromer=:fromer order by f.createTime desc";
			List list = query(hql).setParameter("fromer", owner)
					.setFirstResult(start).setMaxResults(rows).list();
			page.setRows(list);
			hql = "select count(*) from ReceivedFileItem f where f.fromer=:fromer";
			Object lo = query(hql).setParameter("fromer", owner).uniqueResult();
			page.setRecords(Integer.parseInt(lo.toString()));

		} else if (FileType.UPLOADED == ft) {
			QueryField fc = qc.getFields().get(1);
			String folderId = fc.getValue();
			String hql = "from UploadedFileItem f where f.owner=:owner and f.folder=:folder order by f.createTime desc";
			List list = query(hql).setParameter("owner", owner)
					.setParameter("folder", folderId).setFirstResult(start)
					.setMaxResults(rows).list();
			page.setRows(list);
			hql = "select count(*) from UploadedFileItem f where f.owner=:owner and folder=:folder";
			Object lo = query(hql).setParameter("owner", owner).uniqueResult();
			page.setRecords(Integer.parseInt(lo.toString()));

		}
		return page;
	}

}